import tkinter
from tkinter import *
from PIL import Image, ImageTk
import os
import threading
from LogUtils import logger
import random
"""
改造
"""


class ImageGridView(tkinter.Frame):
    """
    使用网格 布局显示图片，并可以多选图片
    @Create by yangshu
    @Create time : 2021.01.13-11:34:39
    """

    def __init__(self, parent, title="图片网格布局", rowCount=4, columnCount=4, length=100):
        """
        图片网格布局
        :param parent: 父组件
        :param imagePaths: 需要显示的图片路径集合
        :param title: 标题名
        :param rowCount: 行数
        :param columnCount: 列数
        :param childHeight: 行宽
        :param childWidth: 行高
        """
        super().__init__(parent)
        self.initState = False  # 是否已经打开过
        self.__rowCount = rowCount  # 行数
        self.__columnCount = columnCount  # 列数
        self.__stop = self.__rowCount * self.__columnCount  # 一页显示照片的个数
        self.__length = length  # 每个照片显示的长度，宽高同样
        self.title = title  # 标题

        # self.imagePathFlagList = self.__fillImagePathFlags(imagePaths)  # 图片路径和是否标注的集合
        self.imagePathFlagList = []  # 核心遍历全局跟着它走

        self.CurrentPageIndex = 0  # 当前所在的页面，最大值为self.countPage - 1

        # ================初始化布局需要的一些东西=======================
        self.checkButtonList = []  # 页面上的check按钮集合

        self.currentPageFlag = []  # 每个按钮是否被选中的集合 内部值是BoolVar()
        for index in range(self.__stop):
            flagVar = BooleanVar()

            self.currentPageFlag.append(flagVar)

        # ================实现多线程预加载的图片=========================
        self.lastPageImages = []  # 上一页展示图片
        self.currentPageImages = []  # 当前页展示图片
        self.nextPageImages = []  # 下一页展示图片
        # ================初始化布局======================
        self.isAllSelect = False
        self.layoutWidgets()


    def layoutWidgets(self):
        """
        组件布局
        :return:
        """
        self.labelFrame = LabelFrame(self, text=self.title, labelanchor=N)
        self.labelFrame.grid(row=0, column=0, rowspan=3, columnspan=3)
        # 初始化网格权重
        for x in range(self.__columnCount):
            for y in range(self.__rowCount):
                self.labelFrame.columnconfigure(x, weight=1)  # 设置列的最小宽度
                self.labelFrame.rowconfigure(y, weight=1)  # 设置行的最小宽度

        # 网格布局CheckButton,
        for index in range(self.__stop):
            checkButton = Checkbutton(self.labelFrame, bd=10, bg="black", onvalue=True, offvalue=False,
                                      selectcolor="#00cec9",
                                      height=self.__length // 13, width=self.__length // 9,
                                      padx=20, pady=5, variable=self.currentPageFlag[index])
            checkButton.grid(row=index // self.__columnCount, column=index % self.__columnCount, rowspan=1,
                             columnspan=1, sticky=W + S + N + E)
            self.checkButtonList.append(checkButton)

        # 底部切换页数的按钮
        self.LastPageBtn = Button(self, text="上一页", height=1, width=8, command=self.onLastPage)
        self.LastPageBtn.grid(row=3, column=0, padx=5, pady=5)
        self.labelPage = StringVar()
        self.labelPage.set("0 / 0")
        self.allSelectLabel = tkinter.Label(self, textvariable=self.labelPage, height=1, width=8, relief="groove")
        self.allSelectLabel.grid(row=3, column=1, padx=5, pady=5)
        self.allSelectLabel.bind("<Button-1>", self.allSelectAndCancel)
        self.NextPageBtn = Button(self, text="下一页", height=1, width=8, command=self.onNextPage)
        self.NextPageBtn.grid(row=3, column=2, padx=5, pady=5)

    def randomSelect(self,count):
        number = len(self.imagePathFlagList)
        if count >= number:
            logger.error("随机选择数量大于总数")
            return
        rmList = random.sample(list(range(number)),count)
        for index in rmList:
            self.imagePathFlagList[index]["flag"] = True
        self.writeCurrentPageFlag()
        self.updateCurrentPage()



    def allSelectAndCancel(self, event):
        """
        全选或者取消全选
        :return:
        """
        self.isAllSelect = not self.isAllSelect
        if self.isAllSelect: #全选
            #TODO:全选

            for index in range(len(self.imagePathFlagList)):  # 遍历所有
                self.imagePathFlagList[index]["flag"] = True
        else:
            for index in range(len(self.imagePathFlagList)):  # 遍历所有
                self.imagePathFlagList[index]["flag"] = False
        self.writeCurrentPageFlag()  # 写入当前页面配置
        self.updateCurrentPage()  # 更新当前页面


    def clearAllImage(self):
        """
        清除所有图片
        :return:
        """
        self.CurrentPageIndex = 0
        self.imagePathFlagList.clear()
        self.initCurrentPageFlag()
        self.currentPageImages.clear()
        self.updateCurrentPage()
        print("全部出栈")


    def initCurrentPageFlag(self):
        """
        重置CurrentPageFlag，全部设为False
        :return:
        """
        for index in range(self.__stop):
            self.currentPageFlag[index].set(False)

    def initCurrentPageImages(self, flag):
        """
        初始化当前页面的图片,定位使用，
        @:param flag: 用于通知线程方法使用那种模式加载
        :return:
        """
        L = []
        for item in self.getPageImagePathAndFlag(self.CurrentPageIndex):
            L.append(resizeImage(item["image"], self.__length))
        self.currentPageImages = L
        threading.Thread(target=self.preloadingImage_Thread_Fun, args=(flag,)).start()

    def getPageImagePathAndFlag(self, pageIndex):
        """
        获取指定页面的所有路径
        :param pageIndex:
        :return:[{"image":imagePath,"flag":True},{"image":imagePath,"flag":False}]
        """
        back_result = []
        if (pageIndex >= self.getPageCount()) or (pageIndex < 0):
            # logger.debug("需要查询的页面超出范围")
            return back_result
        elif pageIndex == self.getPageCount() - 1:  # 如果选取的页面是最后一页
            for item in self.imagePathFlagList[pageIndex * self.__stop:len(self.imagePathFlagList)]:  # 遍历最后一页仅剩余的几张图片
                back_result.append(item)
            return back_result
        else:
            for item in self.imagePathFlagList[pageIndex * self.__stop:pageIndex * self.__stop + self.__stop]:
                back_result.append(item)
            return back_result

    def preloadingImage_Thread_Fun(self, flag):
        """
        预加载，在子线程中
        :param flag: 0预加载第一页，-1预加载上一页，1预加载下一页，2 预加载指定页但不是第一页
        :return:
        """
        if flag == 0:
            '''
            如果是第一页，就只需要预加载当前页面的下一页
            '''
            L = []
            for item in self.getPageImagePathAndFlag(self.CurrentPageIndex + 1):
                L.append(resizeImage(item["image"], self.__length))
            self.nextPageImages = L


        elif flag == -1:
            '''
            如果是点击上一页,待切换完上一页，就只需要加载当前页面的上一页
            '''
            page = self.CurrentPageIndex - 1
            L = []
            for item in self.getPageImagePathAndFlag(page):
                L.append(resizeImage(item["image"], self.__length))
            self.lastPageImages = L
            self.LastPageBtn.config(state=NORMAL)

        elif flag == 1:
            '''
            如果是点击加载下一页，就只需要加载当前页面的下一页
            '''
            page = self.CurrentPageIndex + 1
            L = []
            for item in self.getPageImagePathAndFlag(page):  # 如果加载的页面超过范围，返回为空
                L.append(resizeImage(item["image"], self.__length))
            self.nextPageImages = L
            self.NextPageBtn.config(state=NORMAL)

        elif flag == 2:
            L1 = []
            for item in self.getPageImagePathAndFlag(self.CurrentPageIndex - 1):  # 如果加载的页面超过范围，返回为空
                L1.append(resizeImage(item["image"], self.__length))
            self.lastPageImages = L1
            L2 = []
            for item in self.getPageImagePathAndFlag(self.CurrentPageIndex + 1):  # 如果加载的页面超过范围，返回为空
                L2.append(resizeImage(item["image"], self.__length))
            self.nextPageImages = L2

    def onLastPage(self):
        """
        响应事件，点击上一页
        :return:
        """
        if self.getPageCount() == 0:
            return
        if self.CurrentPageIndex == 0:  # 如果是第一页
            return
        self.LastPageBtn.config(state=DISABLED)
        self.nextPageImages = self.currentPageImages
        self.currentPageImages = self.lastPageImages
        self.readCurrentPageFlag()  # 切换页面之前将本页的选择数据读取到总的选择集合中
        self.CurrentPageIndex = self.CurrentPageIndex - 1  # 切换页面
        self.writeCurrentPageFlag()  # 切换页面之后，更新页面之前，需要初始化当前页面的选择信息。
        self.updateCurrentPage()  # 更新当前页面
        threading.Thread(target=self.preloadingImage_Thread_Fun, args=(-1,)).start()  # 预加载
        # logger.debug("=========================本页执行完毕===========================")

    def onNextPage(self):
        """
        响应事件，点击下一页
        :return:
        """
        if self.getPageCount() == 0:
            return
        if self.CurrentPageIndex >= self.getPageCount() - 1:  # 如果是最后一页
            return

        self.NextPageBtn.config(state=DISABLED)
        self.lastPageImages = self.currentPageImages
        self.currentPageImages = self.nextPageImages

        self.readCurrentPageFlag()
        self.CurrentPageIndex = self.CurrentPageIndex + 1
        self.writeCurrentPageFlag()
        self.updateCurrentPage()  # 更新界面
        threading.Thread(target=self.preloadingImage_Thread_Fun, args=(1,)).start()  # 预加载
        # logger.debug("=========================本页执行完毕===========================")

    def updateCurrentPage(self):
        """
        更新界面,只负责将当前页面的图显示出来,是否选择会更新BoolVar()自动改变
        :return:
        """
        self.labelFrame.config(text= self.title + str(len(self.imagePathFlagList)))
        if self.getPageCount() - 1 <= 0:
            self.labelPage.set(str(self.CurrentPageIndex) + " / " + "0")
        else:
            self.labelPage.set(str(self.CurrentPageIndex) + " / " + str(self.getPageCount() - 1))

        if self.getPageCount() == 0:
            return

        if self.CurrentPageIndex == self.getPageCount() - 1:  # 如果是最后一页
            for (index, image) in enumerate(self.currentPageImages):
                self.checkButtonList[index].config(image=image)
            image = resizeImage("./image/background.png", self.__length)
            for index in range(len(self.currentPageImages), len(self.checkButtonList)):
                self.checkButtonList[index].config(image=image)
        else:
            for (index, image) in enumerate(self.currentPageImages):
                self.checkButtonList[index].config(image=image)

    def getPageCount(self):
        """
        加载主要数据，根据self.imagePathFlagList的变动，需要变动的字段都在这初始化
        :return:
        """
        return calcPageCount(len(self.imagePathFlagList), self.__stop)  # 总共需要多少页面

    def readCurrentPageFlag(self):
        """
        读取当前页面应该勾选的的集合,并赋值到imagePathFlagList
        :return:
        """
        if self.getPageCount() == 0:
            return
        end = self.CurrentPageIndex * self.__stop  # 当前页面的第一个索引
        if self.CurrentPageIndex == self.getPageCount() - 1:  # 如果勾选的是最后一页
            for (index, item) in enumerate(self.currentPageFlag[0:len(self.imagePathFlagList) - end]):
                self.imagePathFlagList[end + index]["flag"] = item.get()
                # logger.debug("读取到当前页中的数据{0}".format(item.get()))
        else:
            for (index, item) in enumerate(self.currentPageFlag):
                self.imagePathFlagList[end + index]["flag"] = item.get()
                # logger.debug("读取到当前页中的数据{0}".format(item.get()))

    def writeCurrentPageFlag(self):
        """
        修改currentPageFlag中的选中信息，从当前imagePathFlagList读取到，
        :return:
        """
        if self.getPageCount() == 0:
            return
        end = self.CurrentPageIndex * self.__stop  # 最后一页的第一个
        if self.CurrentPageIndex == self.getPageCount() - 1:
            self.initCurrentPageFlag()  # 先清空
            for (index, item) in enumerate(self.imagePathFlagList[end: len(self.imagePathFlagList)]):  # 读取最后一页剩余的元素
                self.currentPageFlag[index].set(item["flag"])
                # logger.debug("读取到全部集合中的本页中的数据{0}".format(item["flag"]))
        else:
            for (index, item) in enumerate(self.imagePathFlagList[end: end + self.__stop]):
                self.currentPageFlag[index].set(item["flag"])
                # logger.debug("读取到全部集合中的本页中的数据{0}".format(item["flag"]))

    def __fillImagePathFlags(self, imagePaths):
        """
        将图片路径，进行填充。
        :param imagePaths: 图片路径
        :return: [{"image":imagePath,"flag":True},{"image":imagePath,"flag":False}]
        """
        back_result = []
        for item in imagePaths:
            back_result.append({"image": item, "flag": False})
        return back_result

    # ==================照片列表入栈=======================
    def pushImagePathToImagePathFlags(self, imagePathList):
        """
        添加照片路径到ImagePathFlags中，
        :param imagePathList: 路径集合
        :return:
        """
        print(imagePathList)
        if len(imagePathList) == 0:  # 如果添加是空
            return
        if len(self.imagePathFlagList) == 0:  # 是个空栈
            if self.initState == False:
                for item in self.checkButtonList:
                    item.config(width=self.__length, height=self.__length)  # 重置元素的大小,只能重置一次，多次重置会造成异常，不知道为啥
                    self.initState = True
            for item in imagePathList:  # 添加元素
                self.imagePathFlagList.append({"image": item, "flag": False})
            self.writeCurrentPageFlag()  # 读取默认值赋值给当前页面的选择
            self.initCurrentPageImages(0)  # 初始化当前页面，采用第一种方式，
            self.updateCurrentPage()
        else:  # 如果不是空栈
            self.readCurrentPageFlag()  # 读取当前页面的标注信息存入总的
            for item in imagePathList:  # 添加元素
                self.imagePathFlagList.append({"image": item, "flag": False})
            self.writeCurrentPageFlag()
            self.initCurrentPageImages(1)  # 初始化当前页面，加载下一页
            self.updateCurrentPage()

    def popImagePathFromImagePathFlags(self):
        """
        将选中的图片出栈
        :return: 路径集合
        """
        if self.getPageCount() == 0:
            return
        self.readCurrentPageFlag()  # 读取当前页面的选择信息
        back_result = []
        # logger.debug("检查imagePathFlagList的长度{0}".format(len(self.imagePathFlagList)))
        flag_list = []  # 要出栈的下标集合
        for (index, item) in enumerate(self.imagePathFlagList):
            if item["flag"]:
                back_result.append(item["image"])
                flag_list.append(index)
                # logger.debug("需要出栈的图片的索引{0}".format(index))
        for index in flag_list[::-1]:  # 倒叙输出，不然会出现删除前面的索引变小，导致越界
            # logger.debug("出栈的图片索引{0}".format(index))
            self.imagePathFlagList.pop(index)
        self.initCurrentPageFlag()  # 清空当前页面

        self.CurrentPageIndex = 0
        self.initCurrentPageImages(0)
        self.updateCurrentPage()
        return back_result

    def getAllImagePath(self):
        """
        获取栈中剩余图片列表
        :return:
        """
        back_result = []
        for item in self.imagePathFlagList:
            back_result.append(item["image"])
        return back_result


def calcPageCount(itemCount, stop):
    """
    当有itemCount个元素时，每页显示stop个元素，计算总共需要多少页面
    :param itemCount:
    :param stop:
    :return:
    """
    return (itemCount // stop) + (0 if itemCount % stop == 0 else 1)


def getSpecifiedTypeFileFromDirectory(file_dir, suffixes):
    """
    获取路径下所有具某些指定后缀的图片
    :param file_dir: 根路径
    :param suffixes: 后缀集合
    :return:
    """
    L = []
    for dirpath, dirnames, filenames in os.walk(file_dir):
        for file in filenames:
            if file[0] == ".":
                continue
            if os.path.splitext(file)[1] in suffixes:
                currentPath = os.path.join(dirpath, file)
                L.append(currentPath)
    return L


def resizeImage(imagePath, length):
    image_open = Image.open(imagePath)
    image_open = image_open.resize((length, length))
    return ImageTk.PhotoImage(image_open)


if __name__ == '__main__':
    pass
